package 遍历器;

import 作用域相关.*;
import 抽象语法树节点.*;

public class 定义监听器 extends 抽象语法树基础监听器 {

    public 符号表 符号表;

    private 作用域 当前作用域;

    @Override
    public void 进入编译单元(编译单元 节点) {
        符号表 = new 符号表();
        System.out.println("进入全局作用域");
        节点.作用域 = 符号表.全局作用域;
        当前作用域 = 符号表.全局作用域;
    }

    @Override
    public void 离开编译单元(编译单元 节点) {
        System.out.println("离开全局作用域");
        当前作用域 = null;
    }

    @Override
    public void 进入类定义(类定义 节点) {
        System.out.println("进入类 " + 节点.名称);
        var 类符号 = new 类符号(节点.名称, 当前作用域, null); // 第一次遍历时不解析父类
        当前作用域.定义(类符号);
        节点.作用域 = 当前作用域;
        节点.符号 = 类符号;
        类符号.节点 = 节点;
        当前作用域 = 类符号;
    }

    @Override
    public void 离开类定义(类定义 节点) {
        System.out.println("离开类 " + 节点.名称);
        当前作用域 = 当前作用域.取外围作用域();
    }

    @Override
    public void 进入字段定义(字段定义 节点) {
        System.out.println("定义字段: " + 节点.名称);
        var 类符号 = (类符号) 当前作用域;
        var 字段符号 = new 字段符号(节点.名称, null); // 第一次遍历时不解析类型
        节点.符号 = 字段符号;
        节点.作用域 = 当前作用域;
        类符号.定义(字段符号); 
    }


    @Override
    public void 进入方法定义(方法定义 节点) {
        System.out.println("定义并进入方法 " + 节点.名称);

        var 方法 = new 方法符号(节点.名称, null, 当前作用域); // 第一次遍历时不解析返回类型
        当前作用域.定义(方法);
        节点.符号 = 方法;
        节点.作用域 = 当前作用域;
        方法.节点 = 节点;
        当前作用域 = 方法;
    }

    @Override
    public void 离开方法定义(方法定义 节点) {
        System.out.println("离开方法 " + 节点.名称);
        当前作用域 = 当前作用域.取外围作用域();
    }

    @Override
    public void 进入参数定义(参数定义 节点) {
        System.out.println("定义方法参数: " + 节点.名称);
        
        var 变量 = new 变量符号(节点.名称, null); // 第一次遍历时不解析类型
        节点.符号 = 变量;
        节点.作用域 = 当前作用域;
        当前作用域.定义(变量);
    }


    @Override
    public void 进入代码块(代码块 节点) {
        var 局部作用域 = new 局部作用域(当前作用域);
        当前作用域 = 局部作用域;
        节点.作用域 = 局部作用域;
        System.out.println("进入代码块");
    }

    @Override
    public void 离开代码块(代码块 节点) {
        System.out.println("离开代码块");
        当前作用域 = 当前作用域.取外围作用域();
    }

    @Override
    public void 进入变量定义(变量定义 节点) {
        System.out.println("定义变量: " + 节点.名称);
        var 变量 = new 变量符号(节点.名称, null); // 第一次遍历时不解析类型
        节点.符号 = 变量;
        节点.作用域 = 当前作用域;
        变量.节点 = 节点;
        当前作用域.定义(变量);
    }

    @Override
    public void 进入函数调用(函数调用 节点) {
        节点.idToken = 节点.对象.idToken;
        节点.作用域 = 当前作用域; // todo: 感觉每个节点都得存一下作用域
    }

    @Override
    public void 进入标识符(标识符 节点) {
        节点.作用域 = 当前作用域;
    }

    @Override
    public void 进入成员访问(成员访问 节点) {
        节点.作用域 = 当前作用域;
    }

    @Override
    public void 进入方法调用(方法调用 节点) {
        节点.作用域 = 当前作用域;
    }
}
